#include "sound/sound.h"

#include <stdio.h>
#include <unistd.h>


#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cmath>

#ifdef WINDOWS
#include <windows.h>
#include <conio.h>
#endif

#ifdef FMOD

#include "sound/fmod/wincompat.h"
#include "sound/fmod/fmod.hpp"

void Sound::ERRCHECK(FMOD_RESULT result)
{
    if (result != FMOD_OK)
    {
        printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
        exit(-1);
    }
}


float Sound::RandomFreq(void)
{
	srand(time(NULL));
	//Fréquence audible : 30Hz -> 8000 Hz
	float log_f_low, log_f_high;

	log_f_low = log(60);
	log_f_high = log(5000);
	std::cout << "log low freq=" << log_f_low << "\tlog high freq=" << log_f_high << std::endl;

	int f_diff = (int) ((log_f_high - log_f_low) * PRECISION_FREQ);

	float log_f = log_f_low + (rand() % f_diff) / PRECISION_FREQ;
	float freq = exp(log_f);
	std::cout << "f_diff = " << f_diff << "\tlog_f = " << log_f << "\tfreq = " << freq << std::endl;

	return freq;
}


int Sound::SoundRandom(void)
{
	FMOD::System    *system;
	FMOD::Channel   *channel = 0;
	FMOD::DSP       *dsp     = 0;
	FMOD_RESULT      result;
	int key = 0;
	float frequency;

	result = FMOD::System_Create(&system);
	Sound::ERRCHECK(result);

	result = system->init(32, FMOD_INIT_NORMAL, 0);
	Sound::ERRCHECK(result);

	/*
	Create an oscillator DSP unit for the tone.
	*/
	result = system->createDSPByType(FMOD_DSP_TYPE_OSCILLATOR, &dsp);
	Sound::ERRCHECK(result);
	result = dsp->setParameter(FMOD_DSP_OSCILLATOR_RATE, 440.0f);
	Sound::ERRCHECK(result);

	do
	{
		if (kbhit())
		{
			result = system->playDSP(FMOD_CHANNEL_REUSE, dsp, true, &channel);
		channel->setVolume(0.3f);
		result = dsp->setParameter(FMOD_DSP_OSCILLATOR_TYPE, 0);
		Sound::ERRCHECK(result);
		channel->setPaused(false);
		key = getch();

		channel->getFrequency(&frequency);
		frequency = RandomFreq() * 100;
		channel->setFrequency(frequency);
		}

		system->update();

		float volume, pan;
		bool playing;

		if (channel)
		{
		channel->getFrequency(&frequency);
		channel->getVolume(&volume);
		channel->getPan(&pan);
		channel->isPlaying(&playing);
		}

		printf("Channel %s : Frequency %.1f Volume %.1f Pan %.1f  \r", playing ? "playing" : "stopped", frequency, volume, pan);
		fflush(stdout);

		//Sleep(10);

	} while (key != 27);

    printf("\n");

    /*
        Shut down
    */
    result = dsp->release();
    ERRCHECK(result);
    result = system->close();
    ERRCHECK(result);
    result = system->release();
    ERRCHECK(result);


    return EXIT_SUCCESS;
}

int Sound::SoundGenerator(void)
{
    FMOD::System    *system;
    FMOD::Channel   *channel = 0;
    FMOD::DSP       *dsp     = 0;
    FMOD_RESULT      result;
    int              key = 0;
    unsigned int     version;

    /*
        Create a System object and initialize.
    */
    result = FMOD::System_Create(&system);
    Sound::ERRCHECK(result);

    result = system->getVersion(&version);
    Sound::ERRCHECK(result);

    if (version < FMOD_VERSION)
    {
        printf("Error!  You are using an old version of FMOD %08x.  This program requires %08x\n", version, FMOD_VERSION);
        getch();
        return 0;
    }

    result = system->init(32, FMOD_INIT_NORMAL, 0);
    Sound::ERRCHECK(result);

    /*
        Create an oscillator DSP unit for the tone.
    */
    result = system->createDSPByType(FMOD_DSP_TYPE_OSCILLATOR, &dsp);
    Sound::ERRCHECK(result);
    result = dsp->setParameter(FMOD_DSP_OSCILLATOR_RATE, 440.0f);
    Sound::ERRCHECK(result);

    printf("Press '1' to play a sine wave\n");
    printf("Press '2' to play a square wave\n");
    printf("Press '3' to play a triangle wave\n");
    printf("Press '4' to play a saw wave\n");
    printf("Press '5' to play a white noise\n");
    printf("Press 's' to stop channel\n");
    printf("\n");
    printf("Press 'v'/'V' to change channel volume\n");
    printf("Press 'f'/'F' to change channel frequency\n");
    printf("Press '['/']' to change channel pan\n");
    printf("Press 'Esc' to quit\n");
    printf("\n");

    /*
        Main loop
    */
    do
    {
        if (kbhit())
        {
            key = getch();

            switch (key)
            {
                case '1' :
                {
                    result = system->playDSP(FMOD_CHANNEL_REUSE, dsp, true, &channel);

                    channel->setVolume(0.4f);
                    result = dsp->setParameter(FMOD_DSP_OSCILLATOR_TYPE, 0);
                    Sound::ERRCHECK(result);
                    channel->setPaused(false);
                    break;
                }
                case '2' :
                {
                    result = system->playDSP(FMOD_CHANNEL_REUSE, dsp, true, &channel);

                    channel->setVolume(0.08f);
                    result = dsp->setParameter(FMOD_DSP_OSCILLATOR_TYPE, 1);
                    Sound::ERRCHECK(result);
                    channel->setPaused(false);
                    break;
                }
                case '3' :
                {
                    result = system->playDSP(FMOD_CHANNEL_REUSE, dsp, true, &channel);

                    channel->setVolume(0.10f);
                    result = dsp->setParameter(FMOD_DSP_OSCILLATOR_TYPE, 2);
                    Sound::ERRCHECK(result);
                    channel->setPaused(false);
                    break;
                }
                case '4' :
                {
                    result = system->playDSP(FMOD_CHANNEL_REUSE, dsp, true, &channel);

                    channel->setVolume(0.125f);
                    result = dsp->setParameter(FMOD_DSP_OSCILLATOR_TYPE, 4);
                    Sound::ERRCHECK(result);
                    channel->setPaused(false);
                    break;
                }
                case '5' :
                {
                    result = system->playDSP(FMOD_CHANNEL_REUSE, dsp, true, &channel);

                    channel->setVolume(0.20f);
                    result = dsp->setParameter(FMOD_DSP_OSCILLATOR_TYPE, 5);
                    Sound::ERRCHECK(result);
                    channel->setPaused(false);
                    break;
                }
                case 's' :
                {
                    channel->stop();
                    break;
                }
                case 'v' :
                {
                    float volume;

                    channel->getVolume(&volume);
                    volume -= 0.02f;
                    channel->setVolume(volume);
                    break;
                }
                case 'V' :
                {
                    float volume;

                    channel->getVolume(&volume);
                    volume += 0.02f;
                    channel->setVolume(volume);
                    break;
                }
                case 'f' :
                {
                    float frequency;

                    channel->getFrequency(&frequency);
                    frequency -= 500.0f;
                    channel->setFrequency(frequency);
                    break;
                }
                case 'F' :
                {
                    float frequency;

                    channel->getFrequency(&frequency);
                    frequency += 500.0f;
                    channel->setFrequency(frequency);
                    break;
                }
                case '[' :
                {
                    float pan;

                    channel->getPan(&pan);
                    pan -= 0.1f;
                    channel->setPan(pan);
                    break;
                }
                case ']' :
                {
                    float pan;

                    channel->getPan(&pan);
                    pan += 0.1f;
                    channel->setPan(pan);
                    break;
                }
            }
        }

        system->update();

        {
            float frequency = 0, volume = 0, pan = 0;
            bool playing = false;

            if (channel)
            {
                channel->getFrequency(&frequency);
                channel->getVolume(&volume);
                channel->getPan(&pan);
                channel->isPlaying(&playing);
            }

            printf("Channel %s : Frequency %.1f Volume %.1f Pan %.1f  \r", playing ? "playing" : "stopped", frequency, volume, pan);
            fflush(stdout);
        }

        // Sleep(10);

    } while (key != 27);

    printf("\n");

    /*
        Shut down
    */
    result = dsp->release();
    ERRCHECK(result);
    result = system->close();
    ERRCHECK(result);
    result = system->release();
    ERRCHECK(result);

    return 0;
}
#endif
